#include "stdafx.h"

bool CommandSeries::UnExecute()
{
	for (int i = 0; i < CmdList.GetCount(); i++)
	{
		Command* pCmd  = 0;
		CmdList.GetElement(i+1, pCmd);
		pCmd->UnExecute();
	}
	return true;	
}

//------------- Open File ------------------
void OpenCommand::Init(OpenBuilder* Builder)
{
	currentBuilder = Builder;
}

//------------- Save File ------------------
void SaveCommand::Init(SaveBuilder* Builder)
{
	currentBuilder = Builder;
}


//------------- Write in cell ------------------
void WriteCommand::Init(const CELLPOS& pos, const wstring & str, Sheet* psht, bool Remove)
{
	cellPosition = pos;
	strToWrite = str;
	pSheet = psht;
	removeCell = Remove;
}

bool WriteCommand::Execute()
{
	// Execute and save the memo
	bool reslt =  pSheet->ModifyCellContent(cellPosition.iRow, cellPosition.iCol, strToWrite , memState, removeCell);
	if (memState.Content == strToWrite && strToWrite == TEXT(""))
	{
		return false;
	}
	return reslt;
}

bool WriteCommand::UnExecute()
{
	// Recover the execution
	Cell* pCell = pSheet->FindCell(cellPosition.iRow, cellPosition.iCol);
	if (pCell ==0)
	{
		pCell = pSheet->AddCell(cellPosition.iRow, cellPosition.iCol);
	}
	if (removeCell)
		pCell->SetState(memState, CELL_MF_ALL);
	else
		pCell->SetState(memState);
	return true;
}

//-------------ChangeRowHeight-------------------
void ChangeRowHeightCmd::Init(const int NewHeight, ROW_NUM iRow, Sheet* psht)
{
	this->iRow = iRow;
	newHeight = NewHeight;
	pSheet = psht;
}

bool ChangeRowHeightCmd::Execute()
{
	oldHeight = pSheet->SetRowHeight(iRow, newHeight);
	return true;
}

bool ChangeRowHeightCmd::UnExecute()
{
	pSheet->SetRowHeight(iRow, oldHeight);
	return true;
}

//-------------ChangeColWidth----------------------
void ChangeColWidthCmd::Init(const int NewWidth, COL_NUM iCol, Sheet* psht)
{
	this->iCol = iCol;
	newWidth = NewWidth;
	pSheet = psht;
}

bool ChangeColWidthCmd::Execute()
{
	oldWidth = pSheet->SetColWidth(iCol, newWidth);
	return true;
}

bool ChangeColWidthCmd::UnExecute()
{
	pSheet->SetColWidth(iCol, oldWidth);
	return true;
}

//--------------InsertRow-------------------------
void InsertRowCmd::Init(const ROW_NUM iRow, Sheet* psht)
{
	this->iRow = iRow;
	pSheet = psht;
}

bool InsertRowCmd::Execute()
{
	pSheet->AddRow(iRow);
	return true;
}

bool InsertRowCmd::UnExecute()
{
	pSheet->RemoveRow(iRow);
	return true;
}

//--------------InsertCol-------------------------
void InsertColCmd::Init(const COL_NUM iCol, Sheet* psht)
{
	this->iCol = iCol;
	pSheet = psht;
}

bool InsertColCmd::Execute()
{
	pSheet->AddColumn(iCol);
	return true;
}

bool InsertColCmd::UnExecute()
{
	pSheet->RemoveColumn(iCol);
	return true;
}
//---------------DeleteRow----------------------------
void RemoveRowCmd::Init(const ROW_NUM irow, Sheet* psht)
{
	pSheet = psht;
	iRow = irow;
}

bool RemoveRowCmd::Execute()
{
	if (CmdList.GetCount()!=0)
	{
		CmdList.Clear();
	}
	// Clear all cell first, then remove the row
	RowHeader* pRowHeader = pSheet->AddRowHeaderNode(iRow);
	if (!pRowHeader->Empty())
	{
		Cell* pCell = (Cell*)(pRowHeader->pCellRight);
		do
		{
			CELLPOS pos;
			pos.iRow = pCell->GetRowNumber();
			pos.iCol = pCell->GetColumnNumber();
			pCell = (Cell*)(pCell->pCellRight);
			WriteCommand* pCmd = new WriteCommand();
			pCmd->Init(pos, TEXT(""), pSheet, true);
			if(pCmd->Execute())
				CmdList.Append(pCmd);
			else
				delete pCmd;
		}while(pCell!=0);
	}
	pSheet->RemoveRow(iRow);
	return true;
}

bool RemoveRowCmd::UnExecute()
{
	// Create the Row, then add cell content back
	pSheet->AddRow(iRow);
	CommandSeries::UnExecute();
	return true;
}
//---------------RemoveColumn------------------------
void RemoveColCmd::Init(const COL_NUM icol, Sheet* psht)
{
	pSheet = psht;
	iCol = icol;
}

bool RemoveColCmd::Execute()
{
	if (CmdList.GetCount()!=0)
	{
		CmdList.Clear();
	}
	// Clear all cell first, then remove the column
	ColHeader* pColHeader = pSheet->AddColHeaderNode(iCol);
	if (!pColHeader->Empty())
	{
		Cell* pCell = (Cell*)(pColHeader->pCellBelow);
		do
		{
			CELLPOS pos;
			pos.iRow = pCell->GetRowNumber();
			pos.iCol = pCell->GetColumnNumber();
			pCell = (Cell*)(pCell->pCellBelow);
			WriteCommand* pCmd = new WriteCommand();
			pCmd->Init(pos, TEXT(""), pSheet, true);
			if(pCmd->Execute())
				CmdList.Append(pCmd);
			else
				delete pCmd;
		}while(pCell!=0);
	}
	pSheet->RemoveColumn(iCol);
	return true;
}

bool RemoveColCmd::UnExecute()
{
	// Create the Column, then add cell content back
	pSheet->AddColumn(iCol);
	CommandSeries::UnExecute();
	return true;
}


//---------------DeleteRegionCmd-----------------------
void DeleteRegionCmd::Init(const CELLPOS& start, const CELLPOS& end, Sheet* psht)
{
	iRow_s = min(start.iRow, end.iRow);
	iRow_e = max(start.iRow, end.iRow);
	iCol_s = min(start.iCol, end.iCol);
	iCol_e = max(start.iCol, end.iCol);
	pSheet = psht;
}

bool DeleteRegionCmd::Execute()
{
	if (CmdList.GetCount()!=0)
	{
		for (int i = 0; i < CmdList.GetCount(); i++)
		{
			Command* pCmd  = 0;
			CmdList.GetElement(i+1, pCmd);
			pCmd->Execute();
		}
		return true;
	}
	bool rlt = false;
	for(ROW_NUM iRow = iRow_s; iRow<= iRow_e; iRow++)
	{
		for(COL_NUM iCol = iCol_s; iCol<= iCol_e; iCol++)
		{
			if (!pSheet->FindCell(iRow, iCol))
				continue;
			WriteCommand* pCmd = new WriteCommand();
			CELLPOS pos;
			pos.iRow = iRow; pos.iCol = iCol;
			pCmd->Init(pos, TEXT(""), pSheet);
			if (pCmd->Execute())
			{
				CmdList.Append(pCmd);
				rlt = true;
			}
			else
				delete pCmd;
		}
	}
	return rlt;
}

//-------------------SetSingleColorCmd----------------
void SetSingleColorCmd::Init(const CELLPOS& pos, const COLORREF& color, ModifiedField MF, Sheet* psht)
{
	cellPosition = pos;
	pSheet = psht;
	newColor = color;
	mf = MF;
}

bool SetSingleColorCmd::Execute()
{
	Cell* pCell = 0;
	pCell = pSheet->FindCell(cellPosition.iRow, cellPosition.iCol);
	if (pCell==0)
		pCell = pSheet->AddCell(cellPosition.iRow, cellPosition.iCol);
	mem = pCell->GetState(mf);
	if ((mf & CELL_MF_BKCOLOR) != 0)
		pCell->SetBackColor(newColor);
	if ((mf & CELL_MF_TXCOLOR) != 0)
		pCell->SetTextColor(newColor);
	return true;
}

bool SetSingleColorCmd::UnExecute()
{
	Cell* pCell = pSheet->FindCell(cellPosition.iRow, cellPosition.iCol);
	if (pCell ==0)
	{
		pCell = pSheet->AddCell(cellPosition.iRow, cellPosition.iCol);
	}
	pCell->SetState(mem, mf);
	return true;
}
//------------------SetColor------------------------
void SetColorCmd::Init(const CELLPOS& start, const CELLPOS& end, const COLORREF& color, ModifiedField MF, Sheet* psht, bool clear)
{
	iRow_s = min(start.iRow, end.iRow);
	iRow_e = max(start.iRow, end.iRow);
	iCol_s = min(start.iCol, end.iCol);
	iCol_e = max(start.iCol, end.iCol);
	isClearColor = clear;
	if (!isClearColor)
	{
		mf = MF;
		newColor = color;
	}
	else
	{
		mf = 0;
	}
	pSheet = psht;
}

bool SetColorCmd::Execute()
{
	if (CmdList.GetCount()!=0)
	{
		for (int i = 0; i < CmdList.GetCount(); i++)
		{
			Command* pCmd  = 0;
			CmdList.GetElement(i+1, pCmd);
			pCmd->Execute();
		}
		return true;
	}
	for(ROW_NUM iRow = iRow_s; iRow<= iRow_e; iRow++)
	{
		for(COL_NUM iCol = iCol_s; iCol<= iCol_e; iCol++)
		{
			if (!pSheet->FindCell(iRow, iCol))
				pSheet->AddCell(iRow, iCol);
				//continue;

			// Clear All Style
			if (isClearColor)
			{
				//Clear BK color
				SetSingleColorCmd* pCmdBk = new SetSingleColorCmd();
				CELLPOS pos;
				pos.iRow = iRow; pos.iCol = iCol;
				pCmdBk->Init(pos, CELL_DEFBKCLR, CELL_MF_BKCOLOR, pSheet);
				if (pCmdBk->Execute())
				{
					CmdList.Append(pCmdBk);
				}
				else
					delete pCmdBk;

				//Clear Fore Color
				SetSingleColorCmd* pCmdTx = new SetSingleColorCmd();
				pCmdTx->Init(pos, CELL_DEFTXCLR, CELL_MF_TXCOLOR, pSheet);
				if (pCmdTx->Execute())
				{
					CmdList.Append(pCmdTx);
				}
				else
					delete pCmdTx;

				// Clear H-Align
				SetSingleAlignCmd* pCmdAln = new SetSingleAlignCmd();
				pCmdAln->Init(pos, CELL_DEFHALIGN, CELL_MF_HALIGN, pSheet);
				if (pCmdAln->Execute())
					CmdList.Append(pCmdAln);
				else
					delete pCmdAln;

				// Clear V-Align
				SetSingleAlignCmd* pCmdVAln = new SetSingleAlignCmd();
				pCmdVAln->Init(pos, CELL_DEFVALIGN, CELL_MF_VALIGN, pSheet);
				if (pCmdVAln->Execute())
					CmdList.Append(pCmdVAln);
				else
					delete pCmdVAln;

				// Clear Font
				SetSingleFontCmd* pCmdFont = new SetSingleFontCmd();
				pCmdFont->Init(pos, CELL_DEF_FONT_ATTR, CELL_DEF_FONT_NAME, CELL_MF_FONT, pSheet);
				if (pCmdFont->Execute())
					CmdList.Append(pCmdFont);
				else
					delete pCmdFont;
			}
			else
			{
				SetSingleColorCmd* pCmd = new SetSingleColorCmd();
				CELLPOS pos;
				pos.iRow = iRow; pos.iCol = iCol;
				pCmd->Init(pos, newColor, mf, pSheet);
				if (pCmd->Execute())
				{
					CmdList.Append(pCmd);
				}
				else
					delete pCmd;
			}
		}
	}
	return true;
}

//-------------------SetSingleAlignCmd----------------
void SetSingleAlignCmd::Init(const CELLPOS& pos, const UINT& align, ModifiedField MF, Sheet* psht)
{
	cellPosition = pos;
	pSheet = psht;
	newAlign = align;
	mf = MF;
}

bool SetSingleAlignCmd::Execute()
{
	Cell* pCell = 0;
	pCell = pSheet->FindCell(cellPosition.iRow, cellPosition.iCol);
	if (pCell==0)
		pCell = pSheet->AddCell(cellPosition.iRow, cellPosition.iCol);
	mem = pCell->GetState(mf);
	if ((mf & CELL_MF_HALIGN) != 0)
		pCell->SetTextHAlign(newAlign);
	if ((mf & CELL_MF_VALIGN) != 0)
		pCell->SetTextVAlign(newAlign);
	return true;
}

bool SetSingleAlignCmd::UnExecute()
{
	Cell* pCell = pSheet->FindCell(cellPosition.iRow, cellPosition.iCol);
	if (pCell ==0)
	{
		pCell = pSheet->AddCell(cellPosition.iRow, cellPosition.iCol);
	}
	pCell->SetState(mem, mf);
	return true;
}
//------------------SetAlign------------------------
void SetAlignCmd::Init(const CELLPOS& start, const CELLPOS& end, const UINT& align, ModifiedField MF, Sheet* psht)
{
	iRow_s = min(start.iRow, end.iRow);
	iRow_e = max(start.iRow, end.iRow);
	iCol_s = min(start.iCol, end.iCol);
	iCol_e = max(start.iCol, end.iCol);
	mf = MF;
	newAlign = align;
	pSheet = psht;
}

bool SetAlignCmd::Execute()
{
	if (CmdList.GetCount()!=0)
	{
		for (int i = 0; i < CmdList.GetCount(); i++)
		{
			Command* pCmd  = 0;
			CmdList.GetElement(i+1, pCmd);
			pCmd->Execute();
		}
		return true;
	}
	for(ROW_NUM iRow = iRow_s; iRow<= iRow_e; iRow++)
	{
		for(COL_NUM iCol = iCol_s; iCol<= iCol_e; iCol++)
		{
			if (!pSheet->FindCell(iRow, iCol))
				pSheet->AddCell(iRow, iCol);
			//continue;
			
			SetSingleAlignCmd* pCmd = new SetSingleAlignCmd();
			CELLPOS pos;
			pos.iRow = iRow; pos.iCol = iCol;
			pCmd->Init(pos, newAlign, mf, pSheet);
			if (pCmd->Execute())
			{
				CmdList.Append(pCmd);
			}
			else
				delete pCmd;
		}
	}
	return true;
}

//-------------------Copy Region-------------
void CopyRegionCmd::Init(const CELLPOS& start, const CELLPOS& end, Sheet* psht)
{
	iRow_s = min(start.iRow, end.iRow);
	iRow_e = max(start.iRow, end.iRow);
	iCol_s = min(start.iCol, end.iCol);
	iCol_e = max(start.iCol, end.iCol);
	pSheet = psht;
}

bool CopyRegionCmd::Execute()
{
	wstring buf;
	pSheet->OutPutRegion(iRow_s, iRow_e, iCol_s, iCol_e, buf);
	
	// UNICODE
	HGLOBAL hGlobal = ::GlobalAlloc(GHND | GMEM_SHARE, (buf.length()+1)*sizeof(wchar_t));
	PTSTR  p = (PTSTR)GlobalLock(hGlobal);
	for (size_t i = 0; i < buf.length(); i++)
	{
		p[i] = buf.c_str()[i];
	}
	GlobalUnlock(hGlobal);

	// ANSI
	size_t origsize = wcslen(buf.c_str()) + 1;
	size_t newsize = buf.length() + 1;
	char* nstring = new char[newsize];
	WideCharToMultiByte(CP_ACP,WC_COMPOSITECHECK, buf.c_str(), -1, nstring, newsize, NULL,NULL); 


	HGLOBAL hANSI = ::GlobalAlloc(GHND | GMEM_SHARE, (strlen(nstring)+1)*sizeof(char));
	char* pchar = (char*)GlobalLock(hANSI);
	for (size_t i = 0; i < strlen(nstring); i++)
	{
		pchar[i] = nstring[i];
	}
	GlobalUnlock(hANSI);

	OpenClipboard(NULL);
	EmptyClipboard();
	SetClipboardData(CF_UNICODETEXT, hGlobal);
	SetClipboardData(CF_TEXT, hANSI);
	CloseClipboard();
	delete[] nstring;
	return true;
}

//-------------------Paste-------------
void PasteCmd::Init(const CELLPOS& Pos, Sheet* psht)
{
	pSheet = psht;
	iRow = Pos.iRow;
	iCol = Pos.iCol;
}

bool PasteCmd::Execute()
{
	if (CmdList.GetCount()!=0)
	{
		for (int i = 0; i < CmdList.GetCount(); i++)
		{
			Command* pCmd  = 0;
			CmdList.GetElement(i+1, pCmd);
			pCmd->Execute();
		}
		return true;
	}
	
	if (!::IsClipboardFormatAvailable(CF_UNICODETEXT))
		return false;
	
	::OpenClipboard(NULL);
	HGLOBAL hGlobal = GetClipboardData(CF_UNICODETEXT);
	PTSTR pGlobal = (PTSTR)GlobalLock(hGlobal);

	wstring str(pGlobal);

	GlobalUnlock(hGlobal);
	CloseClipboard();
	
	//wstring tmp;

	int preFlag = -1, currentFlag = -1;
	int nRow = 0, nCol = 0;
	bool newLine = false, skip = false;;
	for (size_t i = 0; i < str.length(); i++)
	{
		newLine = false;
		skip = false;
		if (str.c_str()[i] == TEXT('\t'))
		{
			currentFlag = i;
			skip = true;
		}
		else if(str.c_str()[i] == TEXT('\r'))
		{
			i++;
			preFlag = i;
			currentFlag = i;
			newLine = true;
		}
		if (currentFlag - preFlag > 1)
		{
			wstring sub = str.substr(preFlag+1, currentFlag - preFlag -1);
			
			WriteCommand* pCmd = new WriteCommand();
			CELLPOS pos;
			pos.iRow = iRow + nRow; pos.iCol = iCol + nCol;
			pCmd->Init(pos, sub.c_str(), pSheet);
			if (pCmd->Execute())
			{
				CmdList.Append(pCmd);
			}
			else
			{
				delete pCmd;
			}
		}
		if (skip)
		{
			nCol++;
		}
		if (newLine)
		{
			nCol = 0; 
			nRow++;
		}
		preFlag = currentFlag;
	}
	return true;
}

//-------------------SetSingleFontAttrCmd----------------
void SetSingleFontAttrCmd::Init(const CELLPOS& pos, const UINT& attr, ModifiedField MF, Sheet* psht)
{
	cellPosition = pos;
	pSheet = psht;
	this->attr = attr;
	mf = MF;
}

bool SetSingleFontAttrCmd::Execute()
{
	Cell* pCell = 0;
	pCell = pSheet->FindCell(cellPosition.iRow, cellPosition.iCol);
	if (pCell==0)
		pCell = pSheet->AddCell(cellPosition.iRow, cellPosition.iCol);
	mem = pCell->GetState(mf);
	if ((mf & CELL_MF_FONTATTR) != 0)
	{
		UINT new_attr = mem.fontAttr & attr ? mem.fontAttr-attr : mem.fontAttr+attr;
		if (attr ==  CELL_DEF_FONT_ATTR)
			new_attr = attr;
		pCell->SetFontAttr(new_attr);
	}
	return true;
}

bool SetSingleFontAttrCmd::UnExecute()
{
	Cell* pCell = pSheet->FindCell(cellPosition.iRow, cellPosition.iCol);
	if (pCell ==0)
	{
		pCell = pSheet->AddCell(cellPosition.iRow, cellPosition.iCol);
	}
	pCell->SetState(mem, mf);
	return true;
}
//------------------SetFontAttr------------------------
void SetFontAttrCmd::Init(const CELLPOS& start, const CELLPOS& end, const UINT& attr, ModifiedField MF, Sheet* psht)
{
	iRow_s = min(start.iRow, end.iRow);
	iRow_e = max(start.iRow, end.iRow);
	iCol_s = min(start.iCol, end.iCol);
	iCol_e = max(start.iCol, end.iCol);
	mf = MF;
	this->attr = attr;
	pSheet = psht;
}

bool SetFontAttrCmd::Execute()
{
	if (CmdList.GetCount()!=0)
	{
		for (int i = 0; i < CmdList.GetCount(); i++)
		{
			Command* pCmd  = 0;
			CmdList.GetElement(i+1, pCmd);
			pCmd->Execute();
		}
		return true;
	}
	for(ROW_NUM iRow = iRow_s; iRow<= iRow_e; iRow++)
	{
		for(COL_NUM iCol = iCol_s; iCol<= iCol_e; iCol++)
		{
			if (!pSheet->FindCell(iRow, iCol))
				pSheet->AddCell(iRow, iCol);
			//continue;

			SetSingleFontAttrCmd* pCmd = new SetSingleFontAttrCmd();
			CELLPOS pos;
			pos.iRow = iRow; pos.iCol = iCol;
			pCmd->Init(pos, attr, mf, pSheet);
			if (pCmd->Execute())
			{
				CmdList.Append(pCmd);
			}
			else
				delete pCmd;
		}
	}
	return true;
}

//------------------SetSingleFont------------------------
void SetSingleFontCmd::Init(const CELLPOS& pos, const UINT& attr, const wstring& fontName, ModifiedField MF, Sheet* psht)
{
	cellPosition = pos;
	pSheet = psht;
	this->attr = attr;
	this->newFontName = fontName;
	mf = MF;
}

bool SetSingleFontCmd::Execute()
{
	Cell* pCell = 0;
	pCell = pSheet->FindCell(cellPosition.iRow, cellPosition.iCol);
	if (pCell==0)
		pCell = pSheet->AddCell(cellPosition.iRow, cellPosition.iCol);
	
	AttrCmd.Init(cellPosition, this->attr, CELL_MF_FONTATTR, pSheet);
	AttrCmd.Execute();

	mem = pCell->GetState(mf);
	if ((mf & CELL_MF_FONT) != 0)
	{
		pCell->SetFontName(newFontName);
	}
	return true;
}

bool SetSingleFontCmd::UnExecute()
{
	Cell* pCell = pSheet->FindCell(cellPosition.iRow, cellPosition.iCol);
	if (pCell ==0)
	{
		pCell = pSheet->AddCell(cellPosition.iRow, cellPosition.iCol);
	}
	pCell->SetState(mem, mf);
	AttrCmd.UnExecute();
	return true;
}

//------------------SetFont------------------------
void SetFontCmd::Init(const CELLPOS& start, const CELLPOS& end, const UINT& attr, PTSTR fontName, ModifiedField MF, Sheet* psht)
{
	iRow_s = min(start.iRow, end.iRow);
	iRow_e = max(start.iRow, end.iRow);
	iCol_s = min(start.iCol, end.iCol);
	iCol_e = max(start.iCol, end.iCol);
	mf = MF;
	this->attr = attr;
	this->newFontName = fontName;
	pSheet = psht;
}

bool SetFontCmd::Execute()
{
	if (CmdList.GetCount()!=0)
	{
		for (int i = 0; i < CmdList.GetCount(); i++)
		{
			Command* pCmd  = 0;
			CmdList.GetElement(i+1, pCmd);
			pCmd->Execute();
		}
		return true;
	}
	for(ROW_NUM iRow = iRow_s; iRow<= iRow_e; iRow++)
	{
		for(COL_NUM iCol = iCol_s; iCol<= iCol_e; iCol++)
		{
			if (!pSheet->FindCell(iRow, iCol))
				pSheet->AddCell(iRow, iCol);
			//continue;

			SetSingleFontCmd* pCmd = new SetSingleFontCmd();
			CELLPOS pos;
			pos.iRow = iRow; pos.iCol = iCol;
			pCmd->Init(pos, attr, newFontName, mf, pSheet);
			if (pCmd->Execute())
			{
				CmdList.Append(pCmd);
			}
			else
				delete pCmd;
		}
	}
	return true;
}